package com.lingnan.wanxiangxinxun.service.Impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.lingnan.wanxiangxinxun.constant.redisConstant;
import com.lingnan.wanxiangxinxun.entity.dto.News_client_updateDTO;
import com.lingnan.wanxiangxinxun.entity.dto.News_service_updateDTO;
import com.lingnan.wanxiangxinxun.entity.dto.query.News_queryDTO;
import com.lingnan.wanxiangxinxun.entity.dto.query.News_query_pushByUserLikeDTO;
import com.lingnan.wanxiangxinxun.entity.po.Author;
import com.lingnan.wanxiangxinxun.entity.po.Employee;
import com.lingnan.wanxiangxinxun.entity.po.News;
import com.lingnan.wanxiangxinxun.entity.po.NewsCategory;
import com.lingnan.wanxiangxinxun.entity.vo.common.PageVO;
import com.lingnan.wanxiangxinxun.entity.vo.common.Result;
import com.lingnan.wanxiangxinxun.entity.vo.service.NewsVO;
import com.lingnan.wanxiangxinxun.mapper.NewsMapper;
import com.lingnan.wanxiangxinxun.service.IAuthorService;
import com.lingnan.wanxiangxinxun.service.IEmployeeService;
import com.lingnan.wanxiangxinxun.service.INewsCategoryService;
import com.lingnan.wanxiangxinxun.service.INewsService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 新闻表 服务实现类
 * </p>
 *
 * @author swl
 * @since 2025-03-13
 */
@Slf4j
@Service
public class NewsServiceImpl extends ServiceImpl<NewsMapper, News> implements INewsService {

    @Autowired
    private NewsMapper newsMapper;
    @Autowired
    private INewsCategoryService newsCategoryService;
    @Autowired
    private IAuthorService iAuthorService;
    @Autowired
    private IEmployeeService iEmployeeService;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate_Object; // 存对象的时候用
    @Autowired
    private RedisTemplate<String, String> redisTemplate_String; // 存普通数字或者字符串的时候用
    public Double getScore(String key, Object value) {
        return redisTemplate_Object.opsForZSet().score(key, value);
    }
    public Double getScoreForString(String key, String value) {
        return redisTemplate_String.opsForZSet().score(key, value);
    }

    // 删除某条新闻
    @Override
    public boolean deleteById(Integer id) {

        int res = newsMapper.deleteById(id);

        //TODO:告知作者，新闻因不符合规定已被下架

        return res > 0;
    }

    @Override
    public int updateOneNewsById(News_service_updateDTO newsDTO) {
        News news = new News();

        BeanUtils.copyProperties(newsDTO, news);
        news.setUpdateTime(LocalDateTime.now());
        // 审核通过。如果是新稿，则设置发布时间
        if(newsDTO.getStatus() == 1 && newsDTO.getCheckerId() != null && newsDTO.getPublishTime() == null){
            news.setPublishTime(LocalDateTime.now());
        }
        // 审核通过。但不是新稿（被拒回的），则重置拒绝原因
        if (newsDTO.getStatus() == 1 && newsDTO.getPublishTime() != null){
            news.setRejectReason("");
        }
        // 撤回稿件
        if(newsDTO.getStatus() == 2){
            news.setRejectReason(newsDTO.getRejectReason());
        }

        return newsMapper.updateById(news);
    }


    //修改某条新闻的浏览、点赞、收藏数量
    @Override
    public void updateNewData(News_client_updateDTO newsDTO) {
        newsMapper.updateNewData(newsDTO);
    }


    // 修改某条新闻的点赞数量
    @Override
    public void updateNewData_likeNum(Integer newsId, Integer userId) {
        if(userId == null){
            return; // 如果用户未登录，则不做操作
        }

        //判断当前用户是否已经点赞
        Double score = getScoreForString(redisConstant.USER_LIKE_NEWS_KEY + userId, String.valueOf(newsId));

        //如果未点赞，则可以点赞
        if(score == null){
            //数据库点赞数+1
            boolean isSuccess = update().setSql("like_num = like_num + 1").eq("id", newsId).update();
            //保存用户到redis的zSet集合中(key, value, score)
            if(isSuccess){
                redisTemplate_String.opsForZSet().add(redisConstant.USER_LIKE_NEWS_KEY + userId, String.valueOf(newsId), System.currentTimeMillis());
            }
        }

        //如果已经点赞，则取消点赞
        if(score != null){
            //数据库点赞数-1
            boolean isSuccess = update().setSql("like_num = like_num - 1").eq("id", newsId).update();
            //把用户从redis的zSet集合中移除
            if(isSuccess){
                redisTemplate_String.opsForZSet().remove(redisConstant.USER_LIKE_NEWS_KEY + userId, String.valueOf(newsId));
            }
        }
        // 确保每个用户最多记录30条点赞
        Long size = redisTemplate_String.opsForZSet().size(redisConstant.USER_LIKE_NEWS_KEY + userId); // 获取当前用户的记录总数
        if(size != null && size > 30) { // 如果超过30条，删除最早的记录
            // 假设当前 size = 35，那么size - 31 = 4，则removeRange(key, 0, 4) 会删除索引 0 到 4 的元素（共5个元素），删除后剩下 35-5 = 30 条记录
            redisTemplate_String.opsForZSet().removeRange(redisConstant.USER_LIKE_NEWS_KEY, 0, size - 31);
        }
    }


    // 修改某条新闻的收藏数量
    @Override
    public void updateNewData_favoriteNum(Integer newsId, Integer userId) {
        if(userId == null){
            return; // 如果用户未登录，则不做操作
        }

        //判断当前用户是否已经收藏了该新闻
        Double score = getScoreForString(redisConstant.USER_FAVORITE_NEWS_KEY + userId, String.valueOf(newsId));

        //如果未收藏，则可以收藏
        if(score == null){
            //数据库收藏数+1
            boolean isSuccess = update().setSql("favorite_num = favorite_num + 1").eq("id", newsId).update();
            //保存用户到redis的zSet集合中(key, value, score)
            if(isSuccess){
                redisTemplate_String.opsForZSet().add(redisConstant.USER_FAVORITE_NEWS_KEY + userId, String.valueOf(newsId), System.currentTimeMillis());
            }
        }

        //如果已经收藏，则取消收藏
        if(score != null){
            //数据库收藏数-1
            boolean isSuccess = update().setSql("favorite_num = favorite_num - 1").eq("id", newsId).update();
            //把用户从redis的zSet集合中移除
            if(isSuccess){
                redisTemplate_String.opsForZSet().remove(redisConstant.USER_FAVORITE_NEWS_KEY + userId, String.valueOf(newsId));
            }
        }
    }


    // 修改某条新闻的浏览数量
    @Override
    public void updateNewData_viewNum(Integer newsId, Integer userId) {
        if(userId == null){
            return; // 如果用户未登录，则不记录浏览量
        }

        // 1.判断当前用户是否已经浏览了该新闻
        Double score = getScoreForString(redisConstant.USER_HISTORY_NEWS_KEY + userId, String.valueOf(newsId));
        if(score == null){
            update().setSql("views_num = views_num + 1").eq("id", newsId).update(); // 如果未浏览过，则该新闻的浏览次数+1

            // 2.获取该新闻的新闻类型，使这类新闻的浏览量 + 1（存在redis里面，最多存在3天）
            QueryWrapper<News> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("id", newsId);
            queryWrapper.select("category_id"); // 指定查询的字段
            News news = newsMapper.selectOne(queryWrapper);

            if (news != null && news.getCategoryId() != null) {
                Integer categoryId = news.getCategoryId();
                String redisKey = redisConstant.VARIOUS_TYPES_NEWS_VIEW_NUM_KEY
                        + categoryId + ":"
                        + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));

                // 增加该新闻的浏览量
                redisTemplate_String.opsForValue().increment(redisKey, 1); //increment()方法会将值转换为Long类型并进行原子自增
                // 设置过期时间为3天
                redisTemplate_String.expire(redisKey, 3, TimeUnit.DAYS);
            }
        }



        // 3.添加新记录--会自动覆盖旧记录，可以确保最新的浏览记录在最前
        redisTemplate_String.opsForZSet().add(redisConstant.USER_HISTORY_NEWS_KEY + userId, String.valueOf(newsId), System.currentTimeMillis());

        Long size = redisTemplate_String.opsForZSet().size(redisConstant.USER_HISTORY_NEWS_KEY + userId); // 获取当前用户的记录总数
        cleanOldHistoryAsync(redisConstant.USER_HISTORY_NEWS_KEY + userId, size); // 异步删除过期的记录

//        long sevenDaysAgo = System.currentTimeMillis() - (7 * 24 * 60 * 60 * 1000); // 计算7天前的时间戳
//        // 4.删除7天前的记录
//        redisTemplate_String.opsForZSet().removeRangeByScore(redisConstant.USER_HISTORY_NEWS_KEY + userId, 0, sevenDaysAgo);
//
//        // 5.确保每个用户最多记录30条浏览历史
//        Long size = redisTemplate_String.opsForZSet().size(redisConstant.USER_HISTORY_NEWS_KEY + userId); // 获取当前用户的记录总数
//        if(size != null && size > 30) { // 如果超过30条，删除最早的记录
//            // 假设当前 size = 35，那么size - 31 = 4，则removeRange(key, 0, 4) 会删除索引 0 到 4 的元素（共5个元素），删除后剩下 35-5 = 30 条记录
//            redisTemplate_String.opsForZSet().removeRange(redisConstant.USER_HISTORY_NEWS_KEY, 0, size - 31);
//        }
    }

    @Async
    protected void cleanOldHistoryAsync(String key, Long size) {
        // 清理7天前记录
        redisTemplate_String.opsForZSet().removeRangeByScore(
                key,
                0,
                System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7)
        );

        // 强制限制30条（即使异步执行也能保证最终一致性）
        redisTemplate_String.opsForZSet().removeRange(key, 0, size - 31);
    }


    // 设置头条新闻
    @Override
    public Result setHeadlineNews(Integer newsId, Integer checkerId) {
        if(checkerId == null){
            return Result.error("非法修改！");
        }
        redisTemplate_String.opsForValue().set(redisConstant.HEADLINE_NEWS_KEY, String.valueOf(newsId));
        Integer NewsId = Integer.valueOf(redisTemplate_String.opsForValue().get(redisConstant.HEADLINE_NEWS_KEY));
        return Result.success(NewsId);
    }


    @Override
    public PageVO<NewsVO> getAllInPage(long current, long size) {

        Page<News> p = new Page<>(current, size);
        p.addOrder(OrderItem.desc("create_time"));   // 降序排列

        Page<News> page_MP = this.page(p);
        PageVO<News> pageVO1 = PageVO.of(page_MP);

        // 不仅要返回id，还要返回对应的名字，开始装饰一下要返回的数据
        return decorateNews_fillName(pageVO1);
    }

    @Override
    public PageVO<NewsVO> getInCondition(News_queryDTO newsDTO) {
        News news = new News();
        BeanUtils.copyProperties(newsDTO, news);
        if(newsDTO.getCurrent() == 0 || newsDTO.getSize() == 0){
            newsDTO.setCurrent(1);
            newsDTO.setSize(10);
        }

        // 1. 查询数据
        Page<News> page = new Page<>(newsDTO.getCurrent(), newsDTO.getSize()); // 创建分页对象
        Page<News> page_MP = lambdaQuery()
                .eq(news.getId() != null, News::getId, news.getId())
                .like(news.getTitle() != null, News::getTitle, news.getTitle())
                .eq(news.getCategoryId() != null, News::getCategoryId, news.getCategoryId())
                .eq(news.getAuthorId() != null, News::getAuthorId, news.getAuthorId())
                .eq(news.getStatus() != null, News::getStatus, news.getStatus())

                // 时间区间查询
                .between(newsDTO.getPublishTimeAfter() != null && newsDTO.getPublishTimeBefore() != null,
                        News::getPublishTime,
                        newsDTO.getPublishTimeAfter(), newsDTO.getPublishTimeBefore())
                .ge(newsDTO.getPublishTimeAfter()  != null && newsDTO.getPublishTimeBefore()  == null,
                        News::getPublishTime,
                        newsDTO.getPublishTimeAfter())
                .le(newsDTO.getPublishTimeBefore()  != null && newsDTO.getPublishTimeAfter()  == null,
                        News::getPublishTime,
                        newsDTO.getPublishTimeBefore())

                .orderByDesc(News::getCreateTime)
                .page(page); // 应用分页

        PageVO<News> newsPageVO1 = PageVO.of(page_MP);

        // 2. 不仅要返回id，还要返回对应的名字，开始装饰一下要返回的数据
        return decorateNews_fillName(newsPageVO1);
    }


    // 获取热门新闻
    @Override
    public List<News> getHotNews(Integer size) {
        if(size == null){
            size = 10;
        }
        //查新闻--排序规则如下：
        //最近发布，阅读量多，点赞多

        // 当前时间
        LocalDateTime now = LocalDateTime.now();
        // 30天前的时间
        LocalDateTime thirtyDaysAgo = now.minusDays(30);
        // 动态计算排序值
        String orderExpression = "(DATE(publish_time) * 0.4 + views_num * 0.3 + like_num * 0.3)";


        Random random = new Random();
        int randomIntJudge = random.nextInt(10);
        if(Set.of(0,1,2,3).contains(randomIntJudge)){

            // 生成-1到1之间的随机整数
            double randomInt1 = (random.nextInt(21) - 10) / 10.0; // 将整数除以10，得到-1到1之间的随机数，最多小数点后一位
            double randomInt2 = (random.nextInt(21) - 10) / 10.0;
            double randomInt3 = (random.nextInt(21) - 10) / 10.0;

            orderExpression = "(DATE(publish_time) * " + randomInt1 +
                                " + views_num * " + randomInt2 +
                                " + like_num * " + randomInt3 + ")";
        }

        List<News> recentNews = newsMapper.selectList(
                new QueryWrapper<News>()
                        .eq("status", 1)
                        .ge("publish_time", thirtyDaysAgo)
                        .orderByDesc(orderExpression)     // 按总分排序
                        .last("limit " + size)
        );

        if (recentNews.isEmpty() || recentNews.size() < 10) {
            recentNews = newsMapper.selectList(
                    new QueryWrapper<News>()
                            .eq("status", 1)
                            .orderByDesc(orderExpression)
                            .last("limit " + size)
            );
        }
        // 随机打乱顺序
        Collections.shuffle(recentNews);


        // 获取随机内容
        List<News> randomNews = newsMapper.selectList(
                new QueryWrapper<News>()
                        .eq("status", 1)
                        .ge("id", getMinRandomId()) // 使用随机生成的最小主键范围
                        .orderByAsc("id") // 按主键升序排序
                        .last("limit 5")
        );

        // 实现合并逻辑，确保无重复
        Set<Integer> existingIds = recentNews.stream().map(News::getId).collect(Collectors.toSet());

        // 过滤掉randomNews中已存在的新闻
        List<News> uniqueRandomNews = randomNews.stream().filter(news -> !existingIds.contains(news.getId())).toList();

        // 合并两个列表
        List<News> finalRecommendations = new ArrayList<>(recentNews);
        finalRecommendations.addAll(uniqueRandomNews);

        return finalRecommendations;
    }


    // 为用户推荐新闻
    @Override
    public List<News> pushNews(Integer userId, long current, long size) {
        if(userId == null){ //未登录的游客--推荐热门新闻
            return this.getHotNews(null);
        }

        //1.获取用户最近点赞的[20]条新闻，获取每个类型的新闻的数量
        Set<String> newsIdList = redisTemplate_String.opsForZSet().reverseRange(redisConstant.USER_LIKE_NEWS_KEY + userId, 0, 19);
        List<News_query_pushByUserLikeDTO> newsCategoryNumList;
        if (newsIdList.isEmpty()){ //代表用户还没有点赞的新闻，要做随机热门推荐
            return this.getHotNews(null);
        }else {
            newsCategoryNumList = newsMapper.getNewsIdByUserLike(newsIdList);
        }

        Set<Integer> hotNewsWeight = new HashSet<>();
        hotNewsWeight.add(2);
        hotNewsWeight.add(3);
        if(newsCategoryNumList.size() < 3){ //用户喜好的新闻类型过少，要做热门推荐的比重要大些
            hotNewsWeight.add(4); //加了多一个随机值，没有加之前是10分之2的概率，加了一个数字后就变成10分之3
        }
        int actuallyTotalWeight = 0;
        for(News_query_pushByUserLikeDTO newsCategoryNum :newsCategoryNumList){
            //用户有可能是新用户，点赞的新闻数量没有20条这么多，所以总权重不能固定为20，但是上限是20（上限已在sql层面做了限制）
            actuallyTotalWeight += newsCategoryNum.getNum();
        }

        //2.【出现的数量即权重】，获取各个新闻类型的权重，按照权重从高到低排序，根据类型查询新闻，最多返回size条新闻
        List<News> newsList = new ArrayList<>();
        List<Integer> duplicateNewsIds = new ArrayList<>();
        Page<News> page = new Page<>(1, 10);
        List<NewsCategory> newsCategoryList = newsCategoryService.list(); //所有的新闻类型

        List<String> testList = new ArrayList<>();
        //---开始添加新闻---
        for(int i = 0; i < size; i++){
            if(!newsList.isEmpty()){
                duplicateNewsIds = newsList.stream().map(News::getId).toList(); // 存放已经添加过的新闻id，防止重复推荐新闻
            }

            int randomNum = ThreadLocalRandom.current().nextInt(10); // 生成随机数影响推荐策略

            //根据集合里面的权重，获取新闻
            News randomNews = null;
            if(randomNum == 1){ // 10%的概率(随机数是0到9)推荐一条用户喜欢的领域之外的新闻
                testList.add("之外");
                randomNews = getRandomNews_userUNLike(newsCategoryNumList, newsCategoryList, page, duplicateNewsIds);
            }
            else if (hotNewsWeight.contains(randomNum)) { // 20-30%概率推荐热门新闻。从redis里面拿，redis挂了的话再查数据库
                testList.add("热门");
                List<News> hotNewsList;
                if (redisTemplate_Object.hasKey(redisConstant.HOT_NEWS_KEY)) {
                    //log.info("从redis里面拿热门新闻");
                    hotNewsList = (List<News>) redisTemplate_Object.opsForValue().get(redisConstant.HOT_NEWS_KEY);
                }else {
                    hotNewsList = this.getHotNews(null);
                }
                randomNews = hotNewsList.get(ThreadLocalRandom.current().nextInt(hotNewsList.size()));
            }
            else { //根据用户喜好推荐一条新闻
                testList.add("喜欢");
                randomNews = getRandomNews_userLike(newsCategoryNumList, actuallyTotalWeight, page, duplicateNewsIds);
            }

            // 如果新闻不在列表中，则添加到列表中
            if (!newsList.contains(randomNews) && randomNews != null) {
                newsList.add(randomNews);
            }
//            else { //新闻够多的话是几乎不会出现为null的情况的（因为几乎不会连续查到相同的新闻）
//                log.info("新闻重复或不存在，跳过.{}", randomNews);
//            }
        }

        //log.info("测试用：{}", testList);
        return newsList;
    }


    // 获取相关新闻
    @Override
    public List<News> getRelatedNews(Integer categoryId, Integer currentNewsId) {

        QueryWrapper<News> qw = new QueryWrapper<>();
        qw.eq("status", 1); // 只查已发布的新闻
        qw.eq("category_id", categoryId); // 根据新闻类型查询
        qw.ne("id", currentNewsId); // 排除当前新闻
        qw.last("limit 2"); // 只返回2条数据

        int random = ThreadLocalRandom.current().nextInt(2);
        //50%概率按照时间降序排列，50%按照浏览量，点赞，收藏进行排序
        if(random == 0){
            qw.orderByDesc("create_time");
        }
        else {
            //50%概率按照正常降序，50%概率这三个的权重完全随机
            if(ThreadLocalRandom.current().nextInt(2) == 0){
                qw.orderByDesc("views_num", "like_num", "favorite_num");
            }
            else {
                // 生成-1到1之间的随机整数
                ThreadLocalRandom TLRandom = ThreadLocalRandom.current();
                double randomInt1 = (TLRandom.nextInt(21) - 10) / 10.0; // 将整数除以10，得到-1到1之间的随机数，最多小数点后一位
                double randomInt2 = (TLRandom.nextInt(21) - 10) / 10.0;
                double randomInt3 = (TLRandom.nextInt(21) - 10) / 10.0;

                String orderExpression = "(views_num * " + randomInt1 +
                        " + like_num * " + randomInt2 +
                        " + favorite_num * " + randomInt3 + ")";

                qw.orderByDesc(orderExpression);
            }
        }
        return newsMapper.selectList(qw);
    }


    // 根据新闻id获取新闻列表
    @Override
    public List<News> getNewsListById(List<Integer> ids, Integer userId) {

        //只查一条新闻的情况
        if(ids.size() == 1){
            News news = newsMapper.selectById(ids.get(0));
            if(news == null){
                return null; //文章不存在
            }
            //判断当前用户是否已经点赞
            Double score_like = getScoreForString(redisConstant.USER_LIKE_NEWS_KEY + userId, String.valueOf(ids.get(0)));
            news.setIsLike(score_like != null); //score_like != null代表已经点赞，反之代表未点赞

            //判断当前用户是否已经收藏
            Double score_favorite = getScoreForString(redisConstant.USER_FAVORITE_NEWS_KEY + userId, String.valueOf(ids.get(0)));
            news.setIsFavorite(score_favorite != null);
            return List.of(news);
        }


        return List.of();
    }


    // 根据用户id获取用户点赞的新闻
    @Override
    public List<News> getNewsByUserLike(Integer userId, long current, long size) {
        List<News> newsList = new ArrayList<>();
        // 计算起始和结束索引
        long start = (current - 1) * size;
        long end = start + size - 1;
        //从redis查找
        Set<String> newsIdList = redisTemplate_String.opsForZSet().reverseRange(redisConstant.USER_LIKE_NEWS_KEY + userId, start, end);

        if (!newsIdList.isEmpty()) {
            for (String newsId : newsIdList) {
                News news = newsMapper.selectById(newsId);
                if (news != null) {
                    newsList.add(news);
                }
            }
        }
        return newsList;
    }


    // 根据用户id获取用户收藏的新闻
    @Override
    public List<News> getNewsByUserFavorite(Integer userId, long current, long size) {
        List<News> newsList = new ArrayList<>();
        // 计算起始和结束索引
        long start = (current - 1) * size;
        long end = start + size - 1;
        //从redis查找
        Set<String> newsIdList = redisTemplate_String.opsForZSet().reverseRange(redisConstant.USER_FAVORITE_NEWS_KEY + userId, start, end);

        if (!newsIdList.isEmpty()) {
            for (String newsId : newsIdList) {
                News news = newsMapper.selectById(newsId);
                if (news != null) {
                    newsList.add(news);
                }
            }
        }
        return newsList;
    }


    // 根据用户id获取用户浏览的新闻
    @Override
    public List<News> getNewsByUserHistory(Integer userId, long current, long size) {
        List<News> newsList = new ArrayList<>();
        // 计算起始和结束索引
        long start = (current - 1) * size;
        long end = start + size - 1;

        //从redis查找
        Set<String> newsIdList = redisTemplate_String.opsForZSet()
                .reverseRange(redisConstant.USER_HISTORY_NEWS_KEY + userId, start, end);
        if (!newsIdList.isEmpty()) {
            for (String newsId : newsIdList) {
                News news = newsMapper.selectById(Integer.valueOf(newsId));
                if (news != null) {
                    newsList.add(news);
                }
            }
        }
        return newsList;
    }




    // 添加热门搜索
    @Override
    public Result setHotSearchList(String value, Integer checkerId) {
        Employee employee = iEmployeeService.getById(checkerId);
        if (employee == null) { return Result.error("非法修改！"); }

        /// 将新元素添加到列表头部
        redisTemplate_String.opsForList().leftPush(redisConstant.HOT_SEARCH_KEY, value);
        // 限制列表长度为5，移除多余的元素
        redisTemplate_String.opsForList().trim(redisConstant.HOT_SEARCH_KEY, 0, 4);

        return Result.success();
    }

    // 删除某条热门搜索
    @Override
    public Result deleteHotSearchValue(Integer index, Integer checkerId) {
        Employee employee = iEmployeeService.getById(checkerId);
        if (employee == null) { return Result.error("非法修改！"); }

        try{
            ListOperations<String, String> listOps = redisTemplate_String.opsForList();
            String toRemove = listOps.index(redisConstant.HOT_SEARCH_KEY, index);
            if (toRemove != null) {
                listOps.remove(redisConstant.HOT_SEARCH_KEY, 0, toRemove); // 删除第一个匹配的元素
            }
            return Result.success();
        }
        catch (Exception e){
            return Result.error("删除失败");
        }
    }

    //更改热门搜索顺序（前端拖拽修改）
    @Override
    public Result updateHotSearchListOrder(List<String> valueList, Integer checkerId) {
        Employee employee = iEmployeeService.getById(checkerId);
        if (employee == null) { return Result.error("非法修改！"); }

        try{
            for (String value : valueList){
                redisTemplate_String.opsForList().set(redisConstant.HOT_SEARCH_KEY, valueList.indexOf(value), value);
            }
            return Result.success();
        } catch (Exception e){
            return Result.error("修改失败");
        }
    }

    //修改热门搜索（修改标题文字）
    @Override
    public Result updateHotSearchList(String value, Integer index, Integer checkerId) {
        Employee employee = iEmployeeService.getById(checkerId);
        if (employee == null) { return Result.error("非法修改！"); }

        try{
            redisTemplate_String.opsForList().set(redisConstant.HOT_SEARCH_KEY, index, value); // 修改指定索引位置的元素
            return Result.success();
        } catch (Exception e){
            return Result.error("修改失败");
        }
    }

    // 获取热搜榜
    @Override
    public List<String> getHotSearchList() {
        List<String> hotSearchIdList = redisTemplate_String.opsForList().range(redisConstant.HOT_SEARCH_KEY, 0, -1);
        if (hotSearchIdList.isEmpty()) {
            return Collections.emptyList();
        }
        return hotSearchIdList;
    }




    // 辅助方法：生成随机的最小主键范围
    private Integer getMinRandomId() {
        // 查询表中的最大主键值
        Integer maxId = newsMapper.selectObjs(new QueryWrapper<News>().select("MAX(id)"))
                .stream()
                .findFirst()
                .map(obj -> Integer.parseInt(obj.toString()))
                .orElse(0);
        
        // 生成一个随机的最小主键范围
        return new Random().nextInt(maxId + 1);
    }


    //装饰器--不仅返回id，还要返回对应的名字
    public PageVO<NewsVO> decorateNews_fillName(PageVO<News> pvo_news){
        PageVO<NewsVO> pvo_newsVO = new PageVO<>();
        pvo_newsVO.setTotal(pvo_news.getTotal());
        pvo_newsVO.setPages(pvo_news.getPages());

        //如果本来就没有查到数据，就直接返回
        if(pvo_news.getList().isEmpty()){
            pvo_newsVO.setList(new ArrayList<>()); //空集合
            return pvo_newsVO;
        }

        List<NewsVO> newsVOList = new ArrayList<>();
        List<Integer> categoryIds = new ArrayList<>();
        List<Integer> authorIds = new ArrayList<>();
        List<Integer> checkerIds = new ArrayList<>();

        // 收集所有需要查询的 ID
        for (News news : pvo_news.getList()) {
            categoryIds.add(news.getCategoryId());
            authorIds.add(news.getAuthorId());
            checkerIds.add(news.getCheckerId());
        }

        // 批量查询分类
        List<NewsCategory> newsCategoryList = newsCategoryService.getNewsCategoryListByIds(categoryIds);
        // 批量查询作者
        List<Author> authorList = iAuthorService.getAuthorListByIds(authorIds);
        // 批量查询审核人
        List<Employee> employeeList = iEmployeeService.getEmployeeListByIds(checkerIds);

        // 构建【名字】Map
        Map<Integer, String> categoryMap = newsCategoryList.stream().collect(Collectors.toMap(NewsCategory::getId, NewsCategory::getName));
        Map<Integer, String> authorMap = authorList.stream().collect(Collectors.toMap(Author::getId, Author::getName));
        Map<Integer, String> employeeMap = employeeList.stream().collect(Collectors.toMap(Employee::getId, Employee::getName));

        // 填充数据
        for (News news : pvo_news.getList()) {
            NewsVO newsVO = new NewsVO();
            BeanUtils.copyProperties(news, newsVO);

            newsVO.setCategoryName(categoryMap.get(news.getCategoryId()));
            newsVO.setAuthorName(authorMap.get(news.getAuthorId()));
            newsVO.setCheckerName(employeeMap.get(news.getCheckerId()));

            newsVOList.add(newsVO);
        }

        pvo_newsVO.setList(newsVOList);
        return pvo_newsVO; // 返回装饰好的数据
    }


    //获取用户喜欢的新闻
    public News getRandomNews_userLike(List<News_query_pushByUserLikeDTO> list, int actuallyTotalWeight,
                                       Page<News> page, List<Integer> duplicateNewsIds) {
        Integer categoryId = getRandomCategoryId(list, actuallyTotalWeight); //根据权重，获取新闻类型

        News randomNews = getRandomNews(categoryId, page, duplicateNewsIds); //根据类型查询随机新闻
        return randomNews;
    }


    //获取用户喜欢的领域之外的新闻
    public News getRandomNews_userUNLike(List<News_query_pushByUserLikeDTO> list, List<NewsCategory> newsCategoryList,
                                         Page<News> page, List<Integer> duplicateNewsIds) {

        List<Integer> categoryIdList = new ArrayList<>();
        List<Integer> userLikeCategoryIdList = new ArrayList<>(); //用户喜欢的新闻类型

        for(News_query_pushByUserLikeDTO dto : list){
            userLikeCategoryIdList.add(dto.getCategoryId());
        }
        //放入所有的新闻类型的id
        newsCategoryList.forEach(newsCategory -> {
            categoryIdList.add(newsCategory.getId());
        });

        categoryIdList.removeAll(userLikeCategoryIdList); //移除用户喜欢的新闻类型的id
        if(categoryIdList.isEmpty()){
            return null; //代表所有的新闻类型用户都喜欢，应该按照热门推荐（出现这种情况的可能性很小）
        }

        Integer RandomCategoryId = categoryIdList.get(ThreadLocalRandom.current().nextInt(categoryIdList.size()));

        News randomNews = getRandomNews(RandomCategoryId, page, duplicateNewsIds); //根据类型查询随机新闻
        return randomNews;
    }


    //构建查询条件，返回新闻
    public News getRandomNews(Integer RandomCategoryId, Page<News> page,
                              List<Integer> duplicateNewsIds) {
        LambdaQueryWrapper<News> lambdaWrapper = new LambdaQueryWrapper<>();
        lambdaWrapper.eq(News::getCategoryId, RandomCategoryId)
                .eq(News::getStatus, 1) // 只获取已发布的新闻
                .notIn(duplicateNewsIds != null && !duplicateNewsIds.isEmpty() , News::getId, duplicateNewsIds) //防止重复推荐新闻!!!
                .orderByDesc(News::getPublishTime);

        //会查出来很多这种类型的新闻，所以要随机挑选一条
        List<News> newslist = newsMapper.selectPage(page, lambdaWrapper).getRecords();

        int size = newslist.size();
        if(size != 0){ //新闻很多的情况下一般不会出现为0的情况
            int randomNum = ThreadLocalRandom.current().nextInt(size); //如果size为n，那么randomNum就是[0,n)的随机数
            News news = newslist.get(randomNum); //随机挑选

            return news;
        }
        return null;
    }


    //根据权重，获取新闻类型
    public Integer getRandomCategoryId(List<News_query_pushByUserLikeDTO> items, int totalWeight) {
        Random random = new Random();
        int randomNum = random.nextInt(totalWeight); // 生成一个范围在[0, totalWeight)的随机整数
        int currentWeight = 0;

        //这里使用了区间累加法
        for (News_query_pushByUserLikeDTO item : items) {
            currentWeight += item.getNum(); // 累加权重，直到遍历完权重区间，一定能找得到随机数所在的区间。某个区间越大，随机数落到这个区间的概率就越大
            if (randomNum < currentWeight) {
                return item.getCategoryId();
            }
        }
        return null;
    }

}
